﻿using Castle.MicroKernel.Registration;
using Castle.Windsor;
using System;

namespace Abp.Dependency
{
    public class CastleObjectContainer : IObjectContainer
    {
        public IWindsorContainer Container { get;  }

        public CastleObjectContainer(IWindsorContainer container)
        {
            
            Container = container;
            Container.Register(
                Component.For<IObjectContainer, CastleObjectContainer>().UsingFactoryMethod(() => this)
                );
        }
        public CastleObjectContainer()
        {

            Container = new WindsorContainer();
            Container.Register(
               Component.For<IObjectContainer, CastleObjectContainer>().UsingFactoryMethod(() => this)
               );
        }

        public void RegisterType(Type implementationType, string serviceName = null, LifeStyle life = LifeStyle.Singleton)
        {
            Container.Register(ApplyLifestyle(Component.For(implementationType), serviceName, life));

        }

        public void RegisterType(Type serviceType, Type implementationType, string serviceName = null, LifeStyle life = LifeStyle.Singleton)
        {
            Container.Register(ApplyLifestyle(Component.For(serviceType, implementationType), serviceName, life));

        }

        public TService Resolve<TService>() where TService : class
        {
            return Container.Resolve<TService>();

        }

        public object Resolve(Type serviceType)
        {
            return Container.Resolve(serviceType);
        }

        public TService ResolveNamed<TService>(string serviceName) where TService : class
        {
            return Container.Resolve<TService>(serviceName);
        }

        public object ResolveNamed(string serviceName, Type serviceType)
        {
            return Container.Resolve(serviceName, serviceType);
        }

        public bool TryResolve<TService>(out TService instance) where TService : class
        {
            try
            {
                instance = Resolve<TService>();
                return true;
            }
            catch
            {
                instance = null;
                return false;
            }
        }

        public bool TryResolve(Type serviceType, out object instance)
        {
            try
            {
                instance = Resolve(serviceType);
                return true;
            }
            catch
            {
                instance = null;
                return false;
            }
        }

        public bool TryResolveNamed(string serviceName, Type serviceType, out object instance)
        {
            try
            {
                instance = ResolveNamed(serviceName,serviceType);
                return true;
            }
            catch
            {
                instance = null;
                return false;
            }
        }

        void IObjectContainer.Register<TService, TImplementer>(string serviceName, LifeStyle life)
        {
            Container.Register(ApplyLifestyle(Component.For<TService,TImplementer>(), serviceName, life));

        }

        void IObjectContainer.RegisterInstance<TService, TImplementer>(TImplementer instance, string serviceName)
        {
            Container.Register(ApplyLifestyle(Component.For<TService, TImplementer>().Instance(instance), serviceName, LifeStyle.Singleton));
           
        }

        private static ComponentRegistration<T> ApplyLifestyle<T>(ComponentRegistration<T> registration, string serviceName, LifeStyle life)
          where T : class
        {
            if (!string.IsNullOrWhiteSpace(serviceName))
                 registration.Named(serviceName);
            switch (life)
            {
                case LifeStyle.Transient:
                    return registration.LifestyleTransient();
                case LifeStyle.Singleton:
                    return registration.LifestyleSingleton();
                default:
                    return registration;
            }

        }

    }
}
